<template>
  <div class="uploader-container">
    <div v-bind="$attrs" class="ib uploader" @click.prevent="triggerUpload">
      <slot v-if="uploadState === 'ready'">
        <button class="btn btn-primary">点击上传</button>
      </slot>
      <slot name="loading" v-if="uploadState === 'loading'">
        <button class="btn btn-primary">正在上传...</button>
      </slot>
      <slot name="success" :resp="resp" v-if="uploadState === 'success'">
        <button class="btn btn-primary">上传成功</button>
      </slot>
      <slot name="error" v-if="uploadState === 'error'">
        <button class="btn btn-primary">上传失败</button>
      </slot>
      <div class="ib deleter-container" v-if="uploadState === 'success'">
        <div class="ib deleter" @click.stop="del()">
          <slot name="delete">
            <button class="btn btn-danger">删除</button>
          </slot>
        </div>
      </div>
    </div>
    <input
      ref="fileInput"
      type="file"
      class="file-input d-none"
      @change="handleFileChange"
    />
  </div>
</template>

<script lang='ts'>
import axios from '../libs/axios'
import { defineComponent, PropType, ref, onMounted } from 'vue'

type UploadState = 'ready' | 'loading' | 'success' | 'error';
type beforeUploadValidate = (file: File) => boolean;

export default defineComponent({
  inheritAttrs: false,
  props: {
    action: {
      type: String,
      required: true
    },
    beforeUpload: {
      type: Function as PropType<beforeUploadValidate>
    },
    uploadFile: {
      type: Object
    }
  },
  emits: ['file-upload-success', 'file-upload-error', 'file-upload-delete'],
  setup (props, context) {
    const fileInput = ref<null | HTMLInputElement>(null)

    const uploadState = ref<UploadState>('ready')

    const resp = ref<null | Record<string, unknown>>(null)

    onMounted(() => {
      if (props.uploadFile) {
        uploadState.value = 'success'
        resp.value = props.uploadFile
      }
    })

    const triggerUpload = () => {
      if (uploadState.value !== 'ready') {
        return
      }
      if (fileInput.value) {
        fileInput.value.click()
      }
    }

    const handleFileChange = (e: Event) => {
      const target = e.target as HTMLInputElement
      const files = target.files
      if (files && files.length) {
        if (props.beforeUpload) {
          if (!props.beforeUpload(files[0])) {
            return
          }
        }
        uploadState.value = 'loading'
        const formData = new FormData()
        formData.append('file', files[0])
        axios({
          url: props.action,
          method: 'post',
          data: formData,
          headers: {
            'Content-Type': 'multipart/form-data'
          }
        })
          .then((res) => {
            uploadState.value = 'success'
            resp.value = res.data
            context.emit('file-upload-success', res.data, files[0])
          })
          .catch(() => {
            uploadState.value = 'error'
            context.emit('file-upload-error')
          })
          .finally(() => {
            if (fileInput.value) {
              fileInput.value.value = ''
            }
          })
      }
    }

    const del = () => {
      uploadState.value = 'ready'
      resp.value = null
      context.emit('file-upload-delete')
    }

    return {
      fileInput,
      triggerUpload,
      uploadState,
      handleFileChange,
      resp,
      del
    }
  }
})
</script>
